<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="cYOaX" id="cYOaX"><span data-lake-id="u70e7bce2" id="u70e7bce2">典型回答</span></h1>
  <p data-lake-id="ud8785f34" id="ud8785f34"><span data-lake-id="u5e8b4858" id="u5e8b4858">我们常用的编程语言，比如C语言、Java、Python、Go等都是高级语言，想要把高级语言转变成计算机认识的机器语言有两种方式，分别是编译和解释。</span></p>
  <p data-lake-id="ue5f2d674" id="ue5f2d674"><br></p>
  <p data-lake-id="u679bacbf" id="u679bacbf"><span data-lake-id="u91c178d1" id="u91c178d1">通常认为编译的过程就是通过编译器（</span><span data-lake-id="u789cf2ff" id="u789cf2ff" style="color: rgb(18, 18, 18)">compiler）</span><span data-lake-id="u4eb08ad7" id="u4eb08ad7">把高级语言的源代码，直接</span><strong><span data-lake-id="u4a2aec02" id="u4a2aec02">编译</span></strong><span data-lake-id="u0177449f" id="u0177449f">成可以被机器执行的机器码，交由机器执行。如C语言。</span></p>
  <p data-lake-id="ue84f87c5" id="ue84f87c5"><span data-lake-id="ua36a8519" id="ua36a8519">而解释的过程就是通过解释器（</span><span data-lake-id="u579308f6" id="u579308f6" style="color: rgb(18, 18, 18)">interpreter）</span><span data-lake-id="uf34b617b" id="uf34b617b">直接</span><strong><span data-lake-id="u95e1e94b" id="u95e1e94b">解释</span></strong><span data-lake-id="ubf736612" id="ubf736612">执行，不需要编译成机器语言。如JavaScript。</span></p>
  <p data-lake-id="ufe748d31" id="ufe748d31"><span data-lake-id="u61c1412d" id="u61c1412d">​</span><br></p>
  <p data-lake-id="uba6a7dd2" id="uba6a7dd2"><span data-lake-id="ua818bb85" id="ua818bb85">其实，现在很多的高级语言，已经很难用简单的"编译型"、"解释型"来区分了，尤其是Java，因为他的代码执行过程并不是单一的。</span></p>
  <p data-lake-id="u62ce4260" id="u62ce4260"><span data-lake-id="ud8bed3af" id="ud8bed3af">​</span><br></p>
  <p data-lake-id="ue041b96d" id="ue041b96d"><span data-lake-id="uee744ad8" id="uee744ad8">首先，在Java中，为了实现跨平台和提升运行速度，需要先通过</span><code data-lake-id="uac579aeb" id="uac579aeb"><span data-lake-id="uf013b1e2" id="uf013b1e2">javac</span></code><span data-lake-id="ub994f1d9" id="ub994f1d9">将Java源代码编译成字节码，但是这个字节码并不是机器码，计算机没有办法直接执行，需要通过Java虚拟机来解释执行。 </span></p>
  <p data-lake-id="u233d31f7" id="u233d31f7"><span data-lake-id="u64cb2ec2" id="u64cb2ec2">​</span><br></p>
  <p data-lake-id="uaeaf699b" id="uaeaf699b"><span data-lake-id="u102c1d7b" id="u102c1d7b">但是Java程序在通过解释器进行解释执行的过程中，当JVM发现某个方法或代码块运行特别频繁的时候，就会认为这是“热点代码”（Hot Spot Code)。然后会通过即时编译(JIT)会把部分“热点代码”直接翻译成机器码，而这些机器码就可以被直接执行了。</span></p>
  <p data-lake-id="u7ae767b5" id="u7ae767b5"><span data-lake-id="u2b86d8cb" id="u2b86d8cb">​</span><br></p>
  <p data-lake-id="u4a738768" id="u4a738768" style="text-align: justify"><span data-lake-id="u0bc87f78" id="u0bc87f78">所以，Java语言不能简单的划分成编译型或者解释型，如果非要定义的话，那只能认为，他既是编译型、又是解释型。正常的代码是解释执行的，JIT优化的过程是编译执行的。</span></p>
  <h1 data-lake-id="JaBCV" id="JaBCV"><span data-lake-id="u2b6180db" id="u2b6180db">知识扩展</span></h1>
  <h2 data-lake-id="grp3o" id="grp3o"><span data-lake-id="u9a49b0a1" id="u9a49b0a1">编译</span></h2>
  <p data-lake-id="ufb45c9e8" id="ufb45c9e8"><br></p>
  <p data-lake-id="ucbd2ff09" id="ucbd2ff09"><span data-lake-id="u74d68ee3" id="u74d68ee3">在介绍编译，我们先来简单介绍下编程语言（Programming Language）。编程语言（Programming Language）分为低级语言（Low-level Language）和高级语言（High-level Language）。</span></p>
  <p data-lake-id="u5387f20c" id="u5387f20c"><span data-lake-id="uedd18ced" id="uedd18ced">​</span><br></p>
  <p data-lake-id="uf58fdbe0" id="uf58fdbe0"><span data-lake-id="u0799a67c" id="u0799a67c">机器语言（Machine Language）和汇编语言（Assembly Language）属于低级语言，直接用计算机指令编写程序。</span></p>
  <p data-lake-id="ub656a352" id="ub656a352"><span data-lake-id="u0ac23b48" id="u0ac23b48">​</span><br></p>
  <p data-lake-id="uce860256" id="uce860256"><span data-lake-id="u236a4ea9" id="u236a4ea9">而C、C++、Java、Python等属于高级语言，用语句（Statement）编写程序，语句是计算机指令的抽象表示。</span></p>
  <p data-lake-id="u7b2f55f8" id="u7b2f55f8"><span data-lake-id="u6e8c960e" id="u6e8c960e">​</span><br></p>
  <p data-lake-id="uc5aeb041" id="uc5aeb041"><span data-lake-id="ue95a3757" id="ue95a3757">举个例子，同样一个语句用C语言、汇编语言和机器语言分别表示如下：</span></p>
  <p data-lake-id="uda273bf0" id="uda273bf0"><img src="https://cdn.nlark.com/yuque/0/2022/jpeg/5378072/1667472378969-dfd5e776-aab6-4a30-94af-6067fbfdc71d.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_13%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uf32d2ade" id="uf32d2ade"><span data-lake-id="u48e9869b" id="u48e9869b">​</span><br></p>
  <p data-lake-id="uc7fe31db" id="uc7fe31db"><span data-lake-id="u9eda7a2e" id="u9eda7a2e">计算机只能对数字做运算，符号、声音、图像在计算机内部都要用数字表示，指令也不例外，上表中的机器语言完全由十六进制数字组成。</span></p>
  <p data-lake-id="u12a84553" id="u12a84553"><span data-lake-id="u5c920b2a" id="u5c920b2a">​</span><br></p>
  <p data-lake-id="u352929f6" id="u352929f6"><span data-lake-id="u5fd48868" id="u5fd48868">最早的程序员都是直接用机器语言编程，但是很麻烦，需要查大量的表格来确定每个数字表示什么意思，编写出来的程序很不直观，而且容易出错，于是有了汇编语言，把机器语言中一组一组的数字用助记符（Mnemonic）表示，直接用这些助记符写出汇编程序，然后让汇编器（Assembler）去查表把助记符替换成数字，也就把汇编语言翻译成了机器语言。</span></p>
  <p data-lake-id="ua6f9e0c6" id="ua6f9e0c6"><span data-lake-id="u16f8618f" id="u16f8618f">​</span><br></p>
  <p data-lake-id="udb8dbdc8" id="udb8dbdc8"><span data-lake-id="u357c8cbc" id="u357c8cbc">但是，汇编语言用起来同样比较复杂，后面，就衍生出了Java、C、C++等高级语言。</span></p>
  <p data-lake-id="ucd7ca46f" id="ucd7ca46f"><br></p>
  <p data-lake-id="u663e8d56" id="u663e8d56"><span data-lake-id="u9cb04617" id="u9cb04617">上面提到语言有两种，一种低级语言，一种高级语言。可以这样简单的理解：低级语言是计算机认识的语言、高级语言是程序员认识的语言。</span></p>
  <p data-lake-id="u71d04267" id="u71d04267"><span data-lake-id="u28dd7f41" id="u28dd7f41">​</span><br></p>
  <p data-lake-id="u420177f1" id="u420177f1"><span data-lake-id="u61a6a164" id="u61a6a164">那么如何从高级语言转换成低级语言呢？这个过程其实就是编译。</span></p>
  <p data-lake-id="u7d270340" id="u7d270340"><span data-lake-id="ue1e1b1db" id="ue1e1b1db">​</span><br></p>
  <p data-lake-id="u097777b6" id="u097777b6"><span data-lake-id="ub25208f8" id="ub25208f8">从上面的例子还可以看出，C语言的语句和低级语言的指令之间不是简单的一一对应关系，一条</span><span data-lake-id="u8a31b505" id="u8a31b505">a=b+1</span><span data-lake-id="ued666f65" id="ued666f65">;语句要翻译成三条汇编或机器指令，这个过程称为编译（Compile），由编译器（Compiler）来完成，显然编译器的功能比汇编器要复杂得多。用C语言编写的程序必须经过编译转成机器指令才能被计算机执行，编译需要花一些时间，这是用高级语言编程的一个缺点，然而更多的是优点。首先，用C语言编程更容易，写出来的代码更紧凑，可读性更强，出了错也更容易改正。</span></p>
  <p data-lake-id="u6c377f11" id="u6c377f11"><span data-lake-id="u980b86ab" id="u980b86ab">​</span><br></p>
  <p data-lake-id="u82bd7f5e" id="u82bd7f5e"><strong><span data-lake-id="u8675760f" id="u8675760f">将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序，翻译为计算机能解读、运行的低阶机器语言的程序的过程就是编译。负责这一过程的处理的工具叫做编译器。</span></strong></p>
  <p data-lake-id="uc383ba05" id="uc383ba05"><br></p>
  <p data-lake-id="u6b742fd0" id="u6b742fd0" style="text-align: justify"><span data-lake-id="ue322580f" id="ue322580f">具体在Java语言中，编译又分为前端编译和后端编译，前端编译主要指与源语言有关但与目标机无关的部分，包括词法分析、语法分析、语义分析与中间代码生成。后端编译主要指与目标机有关的部分，包括代码优化和目标代码生成等。</span></p>
  <p data-lake-id="u5f77d69e" id="u5f77d69e" style="text-align: justify"><br></p>
  <p data-lake-id="ud6010a83" id="ud6010a83" style="text-align: justify"><strong><span data-lake-id="u0d2583b6" id="u0d2583b6">我们可以把将</span></strong><code data-lake-id="u62c400c4" id="u62c400c4"><strong><span data-lake-id="u127bb1f3" id="u127bb1f3">.java</span></strong></code><strong><span data-lake-id="uf469a43b" id="uf469a43b">文件编译成</span></strong><code data-lake-id="u72a191f1" id="u72a191f1"><strong><span data-lake-id="uc5f109fd" id="uc5f109fd">.class</span></strong></code><strong><span data-lake-id="ucd2b43e4" id="ucd2b43e4">的编译过程称之为前端编译。把将</span></strong><code data-lake-id="u44da2374" id="u44da2374"><strong><span data-lake-id="u70f480a2" id="u70f480a2">.class</span></strong></code><strong><span data-lake-id="ud9182760" id="ud9182760">文件翻译成机器指令的编译过程称之为后端编译。</span></strong></p>
  <p data-lake-id="u3a282c62" id="u3a282c62" style="text-align: justify"><strong><span data-lake-id="ue31a100c" id="ue31a100c">​</span></strong><br></p>
  <h2 data-lake-id="CPMYn" id="CPMYn"><span data-lake-id="u899bb11d" id="u899bb11d">反编译</span></h2>
  <p data-lake-id="u92e885bf" id="u92e885bf"><span data-lake-id="ub2ed30f7" id="ub2ed30f7">反编译的过程与编译刚好相反，就是将已编译好的编程语言还原到未编译的状态，也就是找出程序语言的源代码。就是将机器看得懂的语言转换成程序员可以看得懂的语言。Java语言中的反编译一般指将</span><code data-lake-id="u229d36be" id="u229d36be"><span data-lake-id="u37c3e463" id="u37c3e463">class</span></code><span data-lake-id="uef48a7b1" id="uef48a7b1">文件转换成</span><code data-lake-id="u09d81215" id="u09d81215"><span data-lake-id="u7b37a1dc" id="u7b37a1dc">java</span></code><span data-lake-id="u92455621" id="u92455621">文件。</span></p>
  <p data-lake-id="u3c53831b" id="u3c53831b"><br></p>
  <p data-lake-id="ua0e6b289" id="ua0e6b289"><span data-lake-id="ude60ecad" id="ude60ecad">有了反编译工具，我们可以做很多事情，最主要的功能就是有了反编译工具，我们就能读得懂Java编译器生成的字节码。如果你想问读懂字节码有啥用，那么我可以很负责任的告诉你，好处大大的。</span></p>
  <p data-lake-id="ua6b3bc4d" id="ua6b3bc4d"><br></p>
  <h2 data-lake-id="rnvRv" id="rnvRv"><span data-lake-id="u7a90f50f" id="u7a90f50f">Java反编译工具</span></h2>
  <p data-lake-id="u6665a138" id="u6665a138"><br></p>
  <p data-lake-id="u7ef4b551" id="u7ef4b551"><span data-lake-id="u004f084c" id="u004f084c">本文主要介绍3个Java的反编译工具：</span><strong><span data-lake-id="u9fa098e2" id="u9fa098e2">javap</span></strong><span data-lake-id="ud719d278" id="ud719d278">、</span><strong><span data-lake-id="u94c074df" id="u94c074df">jad</span></strong><span data-lake-id="u1e6565bd" id="u1e6565bd">和</span><strong><span data-lake-id="u0cb12393" id="u0cb12393">cfr</span></strong></p>
  <p data-lake-id="u3fa9de64" id="u3fa9de64"><br></p>
  <h3 data-lake-id="V5EJK" id="V5EJK"><span data-lake-id="ud3f91b7d" id="ud3f91b7d">javap</span></h3>
  <p data-lake-id="uc628f86f" id="uc628f86f"><br></p>
  <p data-lake-id="u5944c519" id="u5944c519"><code data-lake-id="u775a6ab0" id="u775a6ab0"><span data-lake-id="u26933f1b" id="u26933f1b">javap</span></code><span data-lake-id="u92f9ccce" id="u92f9ccce">是jdk自带的一个工具，可以对代码反编译，也可以查看java编译器生成的字节码。</span><code data-lake-id="ud9b18a8e" id="ud9b18a8e"><span data-lake-id="u1393636f" id="u1393636f">javap</span></code><span data-lake-id="uc2d1699f" id="uc2d1699f">和其他两个反编译工具最大的区别是他生成的文件并不是</span><code data-lake-id="udffc3c31" id="udffc3c31"><span data-lake-id="u2faaa66e" id="u2faaa66e">java</span></code><span data-lake-id="u27e0e184" id="u27e0e184">文件，也不像其他两个工具生成代码那样更容易理解。拿一段简单的代码举例，如我们想分析Java 7中的</span><code data-lake-id="ue4b72d16" id="ue4b72d16"><span data-lake-id="uc450eaac" id="uc450eaac">switch</span></code><span data-lake-id="ucb1bc9c9" id="ucb1bc9c9">是如何支持</span><code data-lake-id="u5db49382" id="u5db49382"><span data-lake-id="u2ff60d45" id="u2ff60d45">String</span></code><span data-lake-id="uf3f52eeb" id="uf3f52eeb">的，我们先有以下可以编译通过的源代码：</span></p>
  <p data-lake-id="u4ef99412" id="u4ef99412"><br></p>
  <pre lang="java"><code>
public class switchDemoString {
    public static void main(String[] args) {
        String str = "world";
        switch (str) {
            case "hello":
                System.out.println("hello");
                break;
            case "world":
                System.out.println("world");
                break;
            default:
                break;
        }
    }
}
</code></pre>
  <p data-lake-id="u54c7407c" id="u54c7407c"><br></p>
  <p data-lake-id="uc02cad70" id="uc02cad70"><span data-lake-id="u8ca4c48c" id="u8ca4c48c">执行以下两个命令：</span></p>
  <p data-lake-id="u89345a81" id="u89345a81"><br></p>
  <pre lang="java"><code>
javac switchDemoString.java
javap -c switchDemoString.class
</code></pre>
  <p data-lake-id="ua1eecf9b" id="ua1eecf9b"><br></p>
  <p data-lake-id="u4aeaa675" id="u4aeaa675"><span data-lake-id="ub51a5092" id="ub51a5092">生成代码如下：</span></p>
  <p data-lake-id="ubddaa4fa" id="ubddaa4fa"><br></p>
  <pre lang="java"><code>
public class com.hollis.suguar.switchDemoString {
  public com.hollis.suguar.switchDemoString();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."&lt;init&gt;":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String world
       2: astore_1
       3: aload_1
       4: astore_2
       5: iconst_m1
       6: istore_3
       7: aload_2
       8: invokevirtual #3                  // Method java/lang/String.hashCode:()I
      11: lookupswitch  { // 2
              99162322: 36
             113318802: 50
               default: 61
          }
      36: aload_2
      37: ldc           #4                  // String hello
      39: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      42: ifeq          61
      45: iconst_0
      46: istore_3
      47: goto          61
      50: aload_2
      51: ldc           #2                  // String world
      53: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      56: ifeq          61
      59: iconst_1
      60: istore_3
      61: iload_3
      62: lookupswitch  { // 2
                     0: 88
                     1: 99
               default: 110
          }
      88: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      91: ldc           #4                  // String hello
      93: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      96: goto          110
      99: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
     102: ldc           #2                  // String world
     104: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     107: goto          110
     110: return
}
</code></pre>
  <p data-lake-id="u247ee49a" id="u247ee49a"><br></p>
  <p data-lake-id="uf360df69" id="uf360df69"><span data-lake-id="u163abab1" id="u163abab1">我个人的理解，</span><code data-lake-id="ud020f501" id="ud020f501"><span data-lake-id="ub07434a3" id="ub07434a3">javap</span></code><span data-lake-id="uc4304c79" id="uc4304c79">并没有将字节码反编译成</span><code data-lake-id="u006e0a8a" id="u006e0a8a"><span data-lake-id="uc90d87ea" id="uc90d87ea">java</span></code><span data-lake-id="ue73dce9b" id="ue73dce9b">文件，而是生成了一种我们可以看得懂字节码。其实javap生成的文件仍然是字节码，只是程序员可以稍微看得懂一些。如果你对字节码有所掌握，还是可以看得懂以上的代码的。其实就是把String转成hashcode，然后进行比较。</span></p>
  <p data-lake-id="u11717cd6" id="u11717cd6"><br></p>
  <p data-lake-id="u327136b9" id="u327136b9"><span data-lake-id="u2e09c93a" id="u2e09c93a">个人认为，一般情况下我们会用到</span><code data-lake-id="u3cd230ab" id="u3cd230ab"><span data-lake-id="uda4584d9" id="uda4584d9">javap</span></code><span data-lake-id="u89b49fde" id="u89b49fde">命令的时候不多，一般只有在真的需要看字节码的时候才会用到。但是字节码中间暴露的东西是最全的，你肯定有机会用到，比如我在分析</span><code data-lake-id="u8711d90f" id="u8711d90f"><span data-lake-id="ubd7f9a31" id="ubd7f9a31">synchronized</span></code><span data-lake-id="u11a37ddc" id="u11a37ddc">的原理的时候就有是用到</span><code data-lake-id="ub3a8c141" id="ub3a8c141"><span data-lake-id="ubd2f6d3f" id="ubd2f6d3f">javap</span></code><span data-lake-id="ucd2f8f53" id="ucd2f8f53">。通过</span><code data-lake-id="ua373cfe0" id="ua373cfe0"><span data-lake-id="ue036d3fd" id="ue036d3fd">javap</span></code><span data-lake-id="uad2c04d6" id="uad2c04d6">生成的字节码，我发现</span><code data-lake-id="u3f863001" id="u3f863001"><span data-lake-id="u4ecfb628" id="u4ecfb628">synchronized</span></code><span data-lake-id="ub24baabb" id="ub24baabb">底层依赖了</span><code data-lake-id="uff680b94" id="uff680b94"><span data-lake-id="u799f1869" id="u799f1869">ACC_SYNCHRONIZED</span></code><span data-lake-id="uf45cee15" id="uf45cee15">标记和</span><code data-lake-id="u4081d756" id="u4081d756"><span data-lake-id="ued93d9f0" id="ued93d9f0">monitorenter</span></code><span data-lake-id="u6d06343e" id="u6d06343e">、</span><code data-lake-id="u0e36db2d" id="u0e36db2d"><span data-lake-id="uf9559a76" id="uf9559a76">monitorexit</span></code><span data-lake-id="u94fc3f15" id="u94fc3f15">两个指令来实现同步。</span></p>
  <p data-lake-id="udd83a816" id="udd83a816"><br></p>
  <h3 data-lake-id="jad" id="jad"><span data-lake-id="u09449434" id="u09449434">jad</span></h3>
  <p data-lake-id="u1cb094b9" id="u1cb094b9"><br></p>
  <p data-lake-id="u8cad4f4d" id="u8cad4f4d"><span data-lake-id="u3f70c919" id="u3f70c919">jad是一个比较不错的反编译工具，只要下载一个执行工具，就可以实现对</span><code data-lake-id="u744d8b50" id="u744d8b50"><span data-lake-id="ud569fabf" id="ud569fabf">class</span></code><span data-lake-id="u22b7ada6" id="u22b7ada6">文件的反编译了。还是上面的源代码，使用jad反编译后内容如下：</span></p>
  <p data-lake-id="u01ab3e13" id="u01ab3e13"><br></p>
  <p data-lake-id="ucbbbe193" id="ucbbbe193"><span data-lake-id="u13a46adb" id="u13a46adb">命令：</span><code data-lake-id="u5eaef4a7" id="u5eaef4a7"><span data-lake-id="u445c3c46" id="u445c3c46">jad switchDemoString.class</span></code></p>
  <p data-lake-id="ud3742d63" id="ud3742d63"><br></p>
  <pre lang="java"><code>
public class switchDemoString
{
    public switchDemoString()
    {
    }
    public static void main(String args[])
    {
        String str = "world";
        String s;
        switch((s = str).hashCode())
        {
        default:
            break;
        case 99162322:
            if(s.equals("hello"))
                System.out.println("hello");
            break;
        case 113318802:
            if(s.equals("world"))
                System.out.println("world");
            break;
        }
    }
}
</code></pre>
  <p data-lake-id="u0de3ceee" id="u0de3ceee"><br></p>
  <p data-lake-id="u26d878ae" id="u26d878ae"><span data-lake-id="u445a81ff" id="u445a81ff">看，这个代码你肯定看的懂，因为这不就是标准的java的源代码么。这个就很清楚的可以看到原来</span><strong><span data-lake-id="u8aaa6a0d" id="u8aaa6a0d">字符串的switch是通过</span></strong><code data-lake-id="ua31deef6" id="ua31deef6"><strong><span data-lake-id="ue8c29a85" id="ue8c29a85">equals()</span></strong></code><strong><span data-lake-id="u81c4221d" id="u81c4221d">和</span></strong><code data-lake-id="u319e0218" id="u319e0218"><strong><span data-lake-id="u4ac16de2" id="u4ac16de2">hashCode()</span></strong></code><strong><span data-lake-id="u7eaa6d58" id="u7eaa6d58">方法来实现的</span></strong><span data-lake-id="u23febccd" id="u23febccd">。</span></p>
  <p data-lake-id="u5a01dae4" id="u5a01dae4"><br></p>
  <p data-lake-id="ufd87c463" id="ufd87c463"><span data-lake-id="uaa0eb8f4" id="uaa0eb8f4">但是，jad已经很久不更新了，在对Java7生成的字节码进行反编译时，偶尔会出现不支持的问题，在对Java 8的lambda表达式反编译时就彻底失败。</span></p>
  <p data-lake-id="ufad941e6" id="ufad941e6"><br></p>
  <h3 data-lake-id="CFR" id="CFR"><span data-lake-id="ud7de56b6" id="ud7de56b6">CFR</span></h3>
  <p data-lake-id="ud1dd3156" id="ud1dd3156"><br></p>
  <p data-lake-id="u81932b64" id="u81932b64"><span data-lake-id="u6a709d65" id="u6a709d65">jad很好用，但是无奈的是很久没更新了，所以只能用一款新的工具替代他，CFR是一个不错的选择，相比jad来说，他的语法可能会稍微复杂一些，但是好在他可以work。</span></p>
  <p data-lake-id="ub655e23e" id="ub655e23e"><br></p>
  <p data-lake-id="uc100eb87" id="uc100eb87"><span data-lake-id="ua0165b55" id="ua0165b55">如，我们使用cfr对刚刚的代码进行反编译。执行一下命令：</span></p>
  <p data-lake-id="u9bc06476" id="u9bc06476"><br></p>
  <pre lang="java"><code>
java -jar cfr_0_125.jar switchDemoString.class --decodestringswitch false
</code></pre>
  <p data-lake-id="ub86a530e" id="ub86a530e"><br></p>
  <p data-lake-id="u393a3d5f" id="u393a3d5f"><span data-lake-id="ubeb497ca" id="ubeb497ca">得到以下代码：</span></p>
  <p data-lake-id="u8853f98a" id="u8853f98a"><br></p>
  <pre lang="java"><code>
public class switchDemoString {
    public static void main(String[] arrstring) {
        String string;
        String string2 = string = "world";
        int n = -1;
        switch (string2.hashCode()) {
            case 99162322: {
                if (!string2.equals("hello")) break;
                n = 0;
                break;
            }
            case 113318802: {
                if (!string2.equals("world")) break;
                n = 1;
            }
        }
        switch (n) {
            case 0: {
                System.out.println("hello");
                break;
            }
            case 1: {
                System.out.println("world");
                break;
            }
        }
    }
}
</code></pre>
  <p data-lake-id="u89e10dda" id="u89e10dda"><br></p>
  <p data-lake-id="ua30d3b14" id="ua30d3b14"><span data-lake-id="u6d6eed30" id="u6d6eed30">通过这段代码也能得到字符串的switch是通过</span><code data-lake-id="u1149853c" id="u1149853c"><span data-lake-id="u18a90161" id="u18a90161">equals()</span></code><span data-lake-id="u42197518" id="u42197518">和</span><code data-lake-id="ud0375be6" id="ud0375be6"><span data-lake-id="u3fcf234e" id="u3fcf234e">hashCode()</span></code><span data-lake-id="ufc83f7ac" id="ufc83f7ac">方法来实现的结论。</span></p>
  <p data-lake-id="u9044f94e" id="u9044f94e"><br></p>
  <p data-lake-id="uba7764bc" id="uba7764bc"><span data-lake-id="u507f0f1c" id="u507f0f1c">相比Jad来说，CFR有很多参数，还是刚刚的代码，如果我们使用以下命令，输出结果就会不同：</span></p>
  <p data-lake-id="ua0b5ac70" id="ua0b5ac70"><br></p>
  <pre lang="java"><code>
java -jar cfr_0_125.jar switchDemoString.class

public class switchDemoString {
    public static void main(String[] arrstring) {
        String string;
        switch (string = "world") {
            case "hello": {
                System.out.println("hello");
                break;
            }
            case "world": {
                System.out.println("world");
                break;
            }
        }
    }
}
</code></pre>
  <p data-lake-id="uf665ff17" id="uf665ff17"><br></p>
  <p data-lake-id="u82d37abe" id="u82d37abe"><span data-lake-id="u1961b348" id="u1961b348">所以</span><code data-lake-id="ue1be7fd0" id="ue1be7fd0"><span data-lake-id="u6b5316d3" id="u6b5316d3">--decodestringswitch</span></code><span data-lake-id="u2d103a05" id="u2d103a05">表示对于switch支持string的细节进行解码。类似的还有</span><code data-lake-id="ub72f4e5e" id="ub72f4e5e"><span data-lake-id="u9c57b057" id="u9c57b057">--decodeenumswitch</span></code><span data-lake-id="u2e080d05" id="u2e080d05">、</span><code data-lake-id="uee63e1c0" id="uee63e1c0"><span data-lake-id="ub984e3ec" id="ub984e3ec">--decodefinally</span></code><span data-lake-id="u255256fc" id="u255256fc">、</span><code data-lake-id="u9bcf9d3e" id="u9bcf9d3e"><span data-lake-id="u52b4feae" id="u52b4feae">--decodelambdas</span></code><span data-lake-id="u38bebeae" id="u38bebeae">等。在我的关于语法糖的文章中，我使用</span><code data-lake-id="u52174d77" id="u52174d77"><span data-lake-id="ua6c02662" id="ua6c02662">--decodelambdas</span></code><span data-lake-id="u1b8351e4" id="u1b8351e4">对lambda表达式警进行了反编译。 源码：</span></p>
  <p data-lake-id="ub93b1569" id="ub93b1569"><br></p>
  <pre lang="java"><code>
public static void main(String... args) {
    List&lt;String&gt; strList = ImmutableList.of("Hollis", "公众号：Hollis", "博客：www.hollischuang.com");

    strList.forEach( s -&gt; { System.out.println(s); } );
}
</code></pre>
  <p data-lake-id="u0e2211ac" id="u0e2211ac"><br></p>
  <p data-lake-id="uc1248b0a" id="uc1248b0a"><code data-lake-id="u23551e5a" id="u23551e5a"><span data-lake-id="u0c2b89b5" id="u0c2b89b5">java -jar cfr_0_125.jar lambdaDemo.class --decodelambdas false</span></code><span data-lake-id="udd5ec795" id="udd5ec795">反编译后代码：</span></p>
  <p data-lake-id="ue6cef05f" id="ue6cef05f"><br></p>
  <pre lang="java"><code>
public static /* varargs */ void main(String ... args) {
    ImmutableList strList = ImmutableList.of((Object)"Hollis", (Object)"\u516c\u4f17\u53f7\uff1aHollis", (Object)"\u535a\u5ba2\uff1awww.hollischuang.com");
    strList.forEach((Consumer&lt;String&gt;)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
}

private static /* synthetic */ void lambda$main$0(String s) {
    System.out.println(s);
}
</code></pre>
  <p data-lake-id="u33899aa7" id="u33899aa7"><br></p>
  <p data-lake-id="u8d89517b" id="u8d89517b"><span data-lake-id="u65aa8b7e" id="u65aa8b7e">CFR还有很多其他参数，均用于不同场景，读者可以使用</span><code data-lake-id="udb88472f" id="udb88472f"><span data-lake-id="u213b26bb" id="u213b26bb">java -jar cfr_0_125.jar --help</span></code><span data-lake-id="u47f5b023" id="u47f5b023">进行了解。这里不逐一介绍了。</span></p>
  <p data-lake-id="uba73eec8" id="uba73eec8"><br></p>
  <h2 data-lake-id="mPtt3" id="mPtt3"><span data-lake-id="u2ea5f73f" id="u2ea5f73f">如何防止反编译</span></h2>
  <p data-lake-id="u43cc2123" id="u43cc2123"><br></p>
  <p data-lake-id="u5bb4a593" id="u5bb4a593"><span data-lake-id="ubccd5371" id="ubccd5371">由于我们有工具可以对</span><code data-lake-id="u48aadac5" id="u48aadac5"><span data-lake-id="u427c5507" id="u427c5507">class</span></code><span data-lake-id="u2530b1a9" id="u2530b1a9">文件进行反编译，所以，对开发人员来说，如何保护Java程序就变成了一个非常重要的挑战。但是，魔高一尺、道高一丈。当然有对应的技术可以应对反编译咯。但是，这里还是要说明一点，和网络安全的防护一样，无论做出多少努力，其实都只是提高攻击者的成本而已。无法彻底防治。</span></p>
  <p data-lake-id="u96a67ea4" id="u96a67ea4"><br></p>
  <p data-lake-id="u1e6673ff" id="u1e6673ff"><span data-lake-id="u8e83bbf8" id="u8e83bbf8">典型的应对策略有以下几种：</span></p>
  <p data-lake-id="ue87c1d28" id="ue87c1d28"><br></p>
  <ul list="u94a0b001">
   <li fid="ua1c6ee1e" data-lake-id="ue9999144" id="ue9999144"><span data-lake-id="u4a71f1b0" id="u4a71f1b0">隔离Java程序 </span></li>
  </ul>
  <ul list="u94a0b001" data-lake-indent="1">
   <li fid="u0d284f80" data-lake-id="u555dba88" id="u555dba88"><span data-lake-id="uae11dac6" id="uae11dac6">让用户接触不到你的Class文件</span></li>
  </ul>
  <ul list="u94a0b001" start="2">
   <li fid="ua1c6ee1e" data-lake-id="uce0acdcf" id="uce0acdcf"><span data-lake-id="uba9350da" id="uba9350da">对Class文件进行加密 </span></li>
  </ul>
  <ul list="u94a0b001" data-lake-indent="1">
   <li fid="uacd057ed" data-lake-id="u4dd3f429" id="u4dd3f429"><span data-lake-id="u7f8bc8c8" id="u7f8bc8c8">提高破解难度</span></li>
  </ul>
  <ul list="u94a0b001" start="3">
   <li fid="ua1c6ee1e" data-lake-id="u5d62f7a9" id="u5d62f7a9"><span data-lake-id="uf161ecb9" id="uf161ecb9">代码混淆 </span></li>
  </ul>
  <ul list="u94a0b001" data-lake-indent="1">
   <li fid="u1d65bf20" data-lake-id="uf9727e33" id="uf9727e33"><span data-lake-id="u5c591dab" id="u5c591dab">将代码转换成功能上等价，但是难于阅读和理解的形式</span></li>
  </ul>
  <p data-lake-id="u1177601e" id="u1177601e"><br></p>
 </body>
</html>